﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using DAL;
using Entidades;

namespace DAO
{
    public class DAOBase
    {
        private const string InsertProcedure = "PR__I";
        private const string UpdateProcedure = "PR__U";
        private const string SelectProcedure = "PR__S";
        private const string SelectAllProcedure = "PR__SA";
        private const string DeleteProcedure = "PR__D";
        private const string SearchProcedure = "PR__SEARCH";

        protected DALBase dalObject;
        protected EntidadBase entidad;

        public DAOBase()
        {
            this.dalObject = new DBSQLServer();
        }

        #region IDAO Members

        public int crear(EntidadBase entidad)
        {
            int id = 0;
            if (entidad == null)
            {
                throw new ArgumentException();
            }

            bool opened = false;

            try
            {
                if (this.dalObject.IsConnectionClosed)
                {
                    opened = true;
                    this.dalObject.Open();
                }

                // Creo el comando
                IDbCommand cmd = null;
                cmd = dalObject.BuildCommand(this.obtainInsertProcedure(), entidad.obtainArray() );

                // Ejecuto el comando de insercion
                int rowCount = cmd.ExecuteNonQuery();
                id = Convert.ToInt32((((IDbDataParameter)cmd.Parameters[0])).Value);
            }
            catch (DbException dbError)
            {
                throw new DALException("Error de base de datos al intentar guardar entidad.", dbError);
            }
            catch (Exception error)
            {
                throw new DALException("Error inesperado al intentar guardar entidad.", error);
            }
            finally
            {
                if (opened)
                {
                    this.dalObject.Close();
                }
            }

            return id;
        }

        public EntidadBase leer(EntidadBase entidad)
        {
            List<EntidadBase> listaEntidades = new List<EntidadBase>();
            bool opened = false;

            try
            {
                if (this.dalObject.IsConnectionClosed)
                {
                    opened = true;
                    this.dalObject.Open();
                }

                // Creo el comando
                IDbCommand cmd = null;
                cmd = dalObject.BuildCommand(this.obtainSelectProcedure(), new object[] { entidad.Id});
                // Ejecuto el comando de lectura
                IDataReader reader = cmd.ExecuteReader();
                listaEntidades = cargar(reader);
            }
            catch (DbException dbError)
            {
                throw new DALException("Error de base de datos al intentar cargar entidad.", dbError);
            }
            catch (Exception error)
            {
                throw new DALException("Error inesperado al intentar cargar entidad.", error);
            }
            finally
            {
                if (opened)
                {
                    this.dalObject.Close();
                }
            }
            return listaEntidades[0];
        }

        public List<EntidadBase> leerTodas()
        {
            List<EntidadBase> listaEntidades = new List<EntidadBase>();
            bool opened = false;

            try
            {
                if (this.dalObject.IsConnectionClosed)
                {
                    opened = true;
                    this.dalObject.Open();
                }

                // Creo el comando
                IDbCommand cmd = null;
                cmd = dalObject.BuildCommand(this.obtainSelectAllProcedure(), null);
                // Ejecuto el comando de lectura
                IDataReader reader = cmd.ExecuteReader();
                listaEntidades = cargar(reader);
            }
            catch (DbException dbError)
            {
                throw new DALException("Error de base de datos al intentar cargar entidad.", dbError);
            }
            catch (Exception error)
            {
                throw new DALException("Error inesperado al intentar cargar entidad.", error);
            }
            finally
            {
                if (opened)
                {
                    this.dalObject.Close();
                }
            }
            return listaEntidades;
        }

        public void actualizar(EntidadBase entidad)
        {
            if (entidad == null)
            {
                throw new ArgumentException("El argumento entity no puede ser nulo.", "entity");
            }

            bool opened = false;

            try
            {
                if (this.dalObject.IsConnectionClosed)
                {
                    opened = true;
                    this.dalObject.Open();
                }

                // Creo el comando
                IDbCommand cmd = null;
                cmd = this.dalObject.BuildCommand(this.obtainUpdateProcedure(), entidad.obtainArray() );

                // Ejecuto el comando de actualizacion
                int rowCount = cmd.ExecuteNonQuery();
            }
            catch (DbException dbError)
            {
                throw new DALException("Error de base de datos al intentar actualizar entidad.", dbError);
            }
            catch (Exception error)
            {
                throw new DALException("Error inesperado al intentar actualizar entidad.", error);
            }
            finally
            {
                if (opened)
                {
                    this.dalObject.Close();
                }
            }
        }

        public void eliminar(EntidadBase entidad)
        {
            if (entidad == null)
            {
                throw new ArgumentException("El argumento entity no puede ser nulo.", "entity");
            }

            bool opened = false;

            try
            {
                if (this.dalObject.IsConnectionClosed)
                {
                    opened = true;
                    this.dalObject.Open();
                }

                // Creo el comando
                IDbCommand cmd = null;
                cmd = this.dalObject.BuildCommand(this.obtainDeleteProcedure(), new object[] { entidad.Id });

                // Ejectuto el comando de eliminacion
                int rowCount = cmd.ExecuteNonQuery();
            }
            catch (DbException dbError)
            {
                throw new DALException("Error de base de datos al intentar eliminar entidad.", dbError);
            }
            catch (Exception error)
            {
                throw new DALException("Error inesperado al intentar eliminar entidad.", error);
            }
            finally
            {
                if (opened)
                {
                    this.dalObject.Close();
                }
            }
        }

        public virtual List<EntidadBase> cargar(IDataReader reader)
        {
            // Creo la lista vacia
            List<EntidadBase> listaEntidades = new List<EntidadBase>();
            // Itero en el data reader y cargo los datos basicos
            while (reader.Read())
            {
                EntidadBase entity = new EntidadBase();
                cargarEntidad(entity, reader);
                listaEntidades.Add(entity);
            }

            reader.Close();

            return listaEntidades;
        }

        public virtual void cargarEntidad(EntidadBase entidad, IDataReader reader)
        {
            this.entidad = entidad;
            // Campo Id
            entidad.Id = Convert.ToInt32(reader["id_investigador"]);
        }

        #endregion

        #region VirtualProcedures

        public virtual string obtainInsertProcedure()
        {
            return InsertProcedure;
        }

        public virtual string obtainDeleteProcedure()
        {
            return DeleteProcedure;
        }

        public virtual string obtainUpdateProcedure()
        {
            return UpdateProcedure;
        }

        public virtual string obtainSelectProcedure()
        {
            return SelectProcedure;
        }

        public virtual string obtainSelectAllProcedure()
        {
            return SelectAllProcedure;
        }

        public virtual string obtainSearchProcedure()
        {
            return SearchProcedure;
        }

        #endregion

    }
}
